#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>

#include "api.h"

struct termios oldtio;
static char cframe_set[] = { FLAG, ADDRESS, C_SET, BCC(C_SET), FLAG };
static char cframe_ua[] = { FLAG, ADDRESS, C_UA, BCC(C_UA), FLAG };
static char cframe_disc[] = { FLAG, ADDRESS, C_DISC, BCC(C_DISC), FLAG };
static char cframe_rr_zero[] = { FLAG, ADDRESS, C_RR0, BCC(C_RR0), FLAG };
static char cframe_rr_one[] = { FLAG, ADDRESS, C_RR1, BCC(C_RR1), FLAG };

unsigned int _rr = 0;

int _alarm = 0;
int _flag = 0;

char * next_cframe_rr() {
	_rr = !_rr;

	return (_rr == 0 ? cframe_rr_zero : cframe_rr_one);
}
void alarmHandler(int signo) {
	_alarm = 1;
	#if DEBUG == 0
	printf("CONNECTION TIMEOUT\n");
	#endif
	signal(SIGALRM, alarmHandler);
}

int decompose_packet(char *packet, int *filesize, char *filename) {

	char packet_type = packet[0];

	if (packet_type == PACKET_CONTROL_START) {
		// read cenas		
		return PACKET_CONTROL_START;
	}
	else if (packet_type == PACKET_CONTROL_END) {
		return PACKET_CONTROL_END;
	}

	return 0;
}

int read_cframe(int fd, char cframe[]) {
	int state = F1_STATE;
	char c;

	if( _flag == SENDER ) {
		_alarm = 0;
		alarm(TIME_OUT);
	}
	
	#if DEBUG == 0
	printf("STATE1: %d\n", state);
	#endif

	while( state <= F2_STATE ) {

		while( read(fd, &c, 1) <= 0 && _alarm == 0 );
		if( _flag == SENDER && _alarm == 1 ) {
			#if DEBUG == 0
			printf("read_cframe(): READING_TIMEOUT\n");
			#endif
			return FRAME_RD_TIMEOUT;
		}

		#if DEBUG == 0
			printf("read_cframe(): BYTE TO READ: %x | %c\n", cframe[state], cframe[state]);
			printf("read_cframe(): BYTE    READ: %x | %c\n", c, c);
		#endif

		if( c != cframe[state] ) state = F1_STATE;
		else ++state;

		#if DEBUG == 0
			printf("read_cframe(): STATE: %d\n", state);
		#endif
	}

	#if DEBUG == 0
	printf("read_cframe(): FRAME_READ\n");
	#endif
	
	return FRAME_READ;
}




int llopen(int port, char flag) {
	struct termios newtio;
	char str[11];

	// Register if receiver or sender
	_flag = flag;
	// Register alarm handler
	signal(SIGALRM, alarmHandler);

	/*
		Open serial port device for reading and writing and not as controlling tty
		because we don't want to get killed if linenoise sends CTRL-C.
	*/

	if (port == 0)
		strcpy(str, "/dev/ttyS0");
	else if (port == 1)
		strcpy(str, "/dev/ttyS1");

	int fd = open(str, O_RDWR | O_NOCTTY | O_NONBLOCK );

	if (fd < 0) { perror(str); exit(-1); }

	if ( tcgetattr(fd,&oldtio) == -1) { /* save current port settings */
		perror("tcgetattr");
		exit(-1);
	}

	bzero(&newtio, sizeof(newtio));
	newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;

	/* set input mode (non-canonical, no echo,...) */
	newtio.c_lflag = 0;

	newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
	newtio.c_cc[VMIN]     = 5;   /* blocking read until 5 chars received */

	/* 
		VTIME e VMIN devem ser alterados de forma a proteger com um temporizador a 
		leitura do(s) proximo(s) caracter(es)
	*/

	tcflush(fd, TCIOFLUSH);
	sleep(1);
	if ( tcsetattr(fd,TCSANOW,&newtio) == -1) {
		perror("tcsetattr");
		exit(-1);
	}

	#if DEBUG == 0
		printf("llopen: New termios structure set.\n");
	#endif

	if( flag == SENDER ) {
		int timeouts = -1;

		#if DEBUG == 0
		printf("sender: llopen: WRITING SET...\n");
		#endif

		do {
			write_cframe(fd, C_SET);

			#if DEBUG == 0
			printf("sender: llopen: SET WRITTEN\n");
			printf("sender: llopen: READING UA...\n");
			#endif
			timeouts++;

		} while( read_cframe(fd, cframe_ua) == FRAME_RD_TIMEOUT && timeouts < MAX_TIMEOUTS);

		if( timeouts >= MAX_TIMEOUTS ) {
		    printf("sender: llopen: Max timeouts reached, exiting...\n");
		    exit(-1);
		}
		
		#if DEBUG == 0
		printf("sender: llopen: UA READ\n");
		#endif

	}
	else if( flag == RECEIVER ) {

		#if DEBUG == 0
		printf("\n\nreceiver: llopen: READING SET...\n");
		#endif

		read_cframe(fd, cframe_set);

		#if DEBUG == 0
		printf("receiver: llopen: SET READ\n");
		printf("receiver: llopen: WRITING UA...\n");
		#endif

		write_cframe(fd, C_UA);

		#if DEBUG == 0
		printf("receiver: llopen: UA WRITTEN\n");
		#endif
	}

	return fd;
}

int llclose(int fd) {
    int timeouts = -1;  
  
    if( _flag == SENDER ) {

        #if DEBUG == 0
        printf("sender: llclose - writing DISC...\n");
        #endif
       
        do {
			write_cframe(fd, C_DISC);
			
			#if DEBUG == 0
			printf("sender: llclose: DISC WRITTEN\n");
			printf("sender: llclose: READING DISC...\n");
			#endif
			
			timeouts++;
    	} while( read_cframe(fd, cframe_disc) == FRAME_RD_TIMEOUT && timeouts < MAX_TIMEOUTS);

		if( timeouts >= MAX_TIMEOUTS ) {
			printf("sender: llclose: Max timeouts reached, exiting...\n");
			return -1;
		}
		#if DEBUG == 0
		printf("sender: llclose: DISC READ\n");
		printf("sender: llclose: WRITING UA\n");
		#endif

		write_cframe(fd, C_UA);

		#if DEBUG == 0
		printf("sender: llclose: UA WRITTEN\n");
		#endif

    }
    else if ( _flag == RECEIVER) {

		#if DEBUG == 0
		printf("\n\nreceiver: llclose: READING DISC...\n");
		#endif

		read_cframe(fd, cframe_disc);

		#if DEBUG == 0
		printf("receiver: llclose: DISC READ\n");
		printf("receiver: llclose: WRITING DISC...\n");
		#endif

		write_cframe(fd, C_DISC);

		#if DEBUG == 0
		printf("receiver: llclose: DISC WRITTEN\n");
		printf("receiver: llclose: READING UA\n");
		#endif

		read_cframe(fd, cframe_ua);
		
		#if DEBUG == 0
		printf("receiver: llclose: UA READ\n");
		#endif
    }

	#if DEBUG == 0
	printf("##### llclose ends #####\n");
	#endif

    tcsetattr(fd,TCSANOW,&oldtio);
	
    return close(fd);
}

void compose_cpacket(char* buf, char* packet, int control)
{
	packet[0] = -control;
	int file_fd = open(buf,O_RDONLY);
	struct stat sta_s;
	fstat(file_fd, &sta_s);
	int size = sta_s.st_size;
	close(file_fd);
	// File size parameter
	packet[1] = FILE_SIZE;
	packet[2] = sizeof(size);

	packet[3] = size >> 24;
	packet[4] = size >> 16;
	packet[5] = size >> 8;
	packet[6] = size;

	// File name parameter
	int i = 0;
	packet[7] = FILE_NAME;
	packet[8] = strlen(buf);
	for(i = 0; i < packet[8]; ++i) {
		packet[i+9] = buf[i];
	}
	
}

void compose_dpacket(char* data, char* packet, unsigned short int data_size)
{
    int i;
    
    packet[0] = 0;
    packet[1] = 0;
    packet[2] = data_size >> 8;
    packet[3] = data_size;   
    
    for(i = HEADER_SIZE; i < data_size + HEADER_SIZE; i++)
    {
        packet[i] = data[i-HEADER_SIZE];
    }

}

int llwrite(int fd, char *buf, int len) {
	if(len == START_CONTROL_PACKET || len == END_CONTROL_PACKET) //send control packet
	{
		int p_len = strlen(buf)+CONTROL_PACKET_PARTIAL_SIZE;
		char packet_c[p_len];
		compose_cpacket(buf, packet_c, len);
		return write_iframe(fd, C_S(_rr), packet_c, p_len);
	}
	else
	{
		char packet_i[len+HEADER_SIZE];
		compose_dpacket(buf, packet_i, len);
		return write_iframe(fd, C_S(_rr), packet_i, len + HEADER_SIZE);
	}	
	return -1;
}

int llread(int fd, char *buffer) {

	
	
	return 0;
}
